package org.aeon.fasttasks.core.scanner;

import org.aeon.fasttasks.core.annotation.TaskScan;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author aeon
 * @createTime 2025/08/12  13:51
 * @description
 */
public class TaskScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    @Override
    @SuppressWarnings("unchecked")
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry,
                                        BeanNameGenerator importBeanNameGenerator) {
        // 读取 @TaskScan 的扫描路径
        AnnotationAttributes attributes = AnnotationAttributes
                .fromMap(importingClassMetadata.getAnnotationAttributes(TaskScan.class.getName()));
        assert attributes != null;
        String[] basePackages = attributes.getStringArray("basePackages");

        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AssignableTypeFilter(attributes.getClass("markerInterface")));
        for (Class<?> annotationClass : attributes.getClassArray("annotationClass")) {
            scanner.addIncludeFilter(new AnnotationTypeFilter((Class<Annotation>) annotationClass));
        }
        scanner.setResourceLoader(resourceLoader);
        // 扫描指定路径下的类
        Map<String, Class<?>> classCache = new ConcurrentHashMap<>();
        Collection<Class<?>> classes = Arrays.stream(basePackages).parallel()
                .flatMap(basePackage -> scanner.findCandidateComponents(basePackage).stream()
                        .map(BeanDefinition::getBeanClassName)
                        .map(className -> classCache.computeIfAbsent(className, name -> {
                            try {
                                return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
                            } catch (ClassNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                        }))
                )
                .collect(Collectors.toSet());
        // 注册扫描结果
        if (registry.containsBeanDefinition(TaskScanResult.NAME)) {
            BeanDefinition bd = registry.getBeanDefinition(TaskScanResult.NAME);
            ConstructorArgumentValues args = bd.getConstructorArgumentValues();
            ConstructorArgumentValues.ValueHolder valueHolder = args.getGenericArgumentValue(Collection.class);
            if (valueHolder != null && valueHolder.getValue() instanceof Collection) {
                ((Collection<Class<?>>) valueHolder.getValue()).addAll(classes);
            }
        } else {
            RootBeanDefinition def = new RootBeanDefinition(TaskScanResult.class);
            def.getConstructorArgumentValues().addGenericArgumentValue(classes);
            registry.registerBeanDefinition(TaskScanResult.NAME, def);
        }
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

}
